#include "TerrainData.h"

#define SAFE_STR(p) ( (p) ? (p) : "")

const uint32 TERRAIN_HEIGHTMAP_MAGIC = 'FEHM';
const uint32 TERRAIN_HEIGHTMAP_VERSION = 0x00100000;

const uint32 TERRAIN_LIGHTMAP_MAGIC = 'FELM';
const uint32 TERRAIN_LIGHTMAP_VERSION = 0x00100002;

const uint32 TERRAIN_GRIDINFO_MAGIC = 'FEGI';
const uint32 TERRAIN_GRIDINFO_VERSION = 0x00100002;

CTerrainData::CTerrainData(CSceneData* pSceneData)
: m_pSceneData(pSceneData)
{
}

CTerrainData::~CTerrainData(void)
{
}

//Load data from .terrain
void CTerrainData::LoadTerrainData(std::string& strFile)
{
	m_strTerrainFile = strFile;
	TiXmlDocument doc(strFile.c_str());

	//base info
	TiXmlElement* PTerrain = doc.FirstChildElement("Terrain");
	if(PTerrain)
	{
		m_strName = SAFE_STR(PTerrain->Attribute("name"));
		PTerrain->QueryIntAttribute("tileSize", (int*)&m_nTileSize);
		PTerrain->QueryIntAttribute("xsize", (int*)&m_nXSize);
		PTerrain->QueryIntAttribute("zsize", (int*)&m_nZSize);
	

		m_vec3Scale = XML_LoadVector3(PTerrain->FirstChildElement("scale"));
		Ogre::Vector3 vec = XML_LoadVector3(PTerrain->FirstChildElement("centre"));
		m_vec3Position = Ogre::Vector3(vec.x - m_vec3Scale.x * m_nXSize,
										vec.y,
										vec.z - m_vec3Scale.z * m_nZSize);

		TiXmlElement* pHeightMap = PTerrain->FirstChildElement("heightmap");
		if(pHeightMap)
		{
			m_strHeightMapName = SAFE_STR(pHeightMap->Attribute("name"));
			m_strHeightMapFormate = SAFE_STR(pHeightMap->Attribute("type"));
		}
	}

	_updateDerivedFields();

	_LoadHeightMap();
}

void CTerrainData::_LoadHeightMap()
{
	m_vecHeightmap.resize((m_nXSize+1)*(m_nZSize+1));

	if(m_strHeightMapName.empty())
	{
		std::fill(m_vecHeightmap.begin(), m_vecHeightmap.end(), (Real)0);
	}
	else if(m_strHeightMapFormate == "standard")
	{
		//Ogre::DataStreamListPtr stream = Ogre::ResourceGroupManager::getSingleton().openResource(m_strHeightMapName);
		FILE* pHeightMapFile = fopen(m_strHeightMapName.c_str(), "rb");
		if(pHeightMapFile)
		{
			u32 header[4];
			fread(&header[0], 4, 4, pHeightMapFile);
			if (header[0] != TERRAIN_HEIGHTMAP_MAGIC)
			{
				OGRE_EXCEPT(Ogre::Exception::ERR_INVALIDPARAMS,
					"Invalid magic of heightmap file '" + m_strHeightMapName + "'while loading terrain '" + m_strName + "'.",
					"TerrainData::load");
			}
			if (header[1] != TERRAIN_HEIGHTMAP_VERSION)
			{
				OGRE_EXCEPT(Ogre::Exception::ERR_INVALIDPARAMS,
					"Invalid version of heightmap file '" + m_strHeightMapName + "'while loading terrain '" + m_strName + "'.",
					"TerrainData::load");
			}
			if (header[2] != m_nXSize+1 || header[3] != m_nZSize+1)
			{
				OGRE_EXCEPT(Ogre::Exception::ERR_INVALIDPARAMS,
					"Dimensions of heightmap file '" + m_strHeightMapName + "' doesn't match the dimensions of the terrain '" + m_strName + "'.",
					"TerrainData::load");
			}
			
			fread(&m_vecHeightmap[0], 4, m_vecHeightmap.size(), pHeightMapFile);
		}
		fclose(pHeightMapFile);
	}
}

void CTerrainData::_updateDerivedFields(void)
{
	mInvScale.x = 1.0 / m_vec3Scale.x;
	mInvScale.y = 1.0 / m_vec3Scale.y;
	mInvScale.z = 1.0 / m_vec3Scale.z;

	mCentre.x = m_vec3Position.x + m_vec3Scale.x * m_nXSize / 2;
	mCentre.y = m_vec3Position.y;
	mCentre.z = m_vec3Position.z + m_vec3Scale.z * m_nZSize / 2;

	mMinX = m_vec3Position.x + m_vec3Scale.x * 0;
	mMaxX = m_vec3Position.x + m_vec3Scale.x * m_nXSize;
	mMinZ = m_vec3Position.z + m_vec3Scale.z * 0;
	mMaxZ = m_vec3Position.z + m_vec3Scale.z * m_nZSize;

	mNumTilePerX = (m_nXSize - 1) / m_nTileSize + 1;
	mNumTilePerZ = (m_nZSize - 1) / m_nTileSize + 1;

	// Allocate buffers
	m_vecHeightmap.resize((m_nXSize + 1) * (m_nZSize + 1));

	m_BoundBox.setNull();
}

const Ogre::AxisAlignedBox& CTerrainData::getBoundBox() const
{
	if (m_BoundBox.isNull())
	{
		if (!m_vecHeightmap.empty())
		{
			RealArray::const_iterator it = m_vecHeightmap.begin();
			Real minHeight = *it;
			Real maxHeight = *it;
			while (++it != m_vecHeightmap.end())
			{
				Real height = *it;
				if (minHeight > height)
					minHeight = height;
				else if (maxHeight < height)
					maxHeight = height;
			}
			m_BoundBox.setExtents(mMinX, m_vec3Position.y + m_vec3Scale.y * minHeight, mMinZ,
				mMaxX, m_vec3Position.y + m_vec3Scale.y * maxHeight, mMaxZ);
		}
	}
	else
	{
		if (m_vecHeightmap.empty())
			m_BoundBox.setNull();
	}

	return m_BoundBox;
}
